#ifndef __PERSON_HPP__
#define __PERSON_HPP__

#include <string>
#include <sstream>
using std::string;
using std::stringstream;

#include "Date.hpp"
#include "ArrayList.hpp"

const int kGenderUndefined = -1;
const int kGenderFemale = 0;
const int kGenderMale = 1;
const int kGenderFuta = 2;
// const int kGenderMTF = 10;
// const int kGenderFTM = 11;

class Person
{
protected:
    string _name;
    int _gender;
    Date _birthday;
    Date _deathday;
    string _job;

    Person* _pFather;
    Person* _pMother;

    ArrayList<Person*> _spouses;  // TODO 人是不能重复的，因此要改为Set
    ArrayList<Person*> _children;  // 同上

public:
    Person(string name, const int &gender, const Date &birthday, const Date &deathday, string job, Person *father = nullptr, Person *mother = nullptr);

    void init(string name, const int &gender, const Date &birthday, const Date &deathday, string job, Person *father = nullptr, Person *mother = nullptr);

    ArrayList<Person*> getChildren(Person *spouse = nullptr);
    void addChild(Person *child, Person *spouse = nullptr);

    void marry(Person *spouse);
    void addSpouse(Person *spouse);

    Person* getFather();
    Person* getMother();
    void setFather(Person *pa);
    void setMother(Person *ma);

    string toString();
};

Person::Person(string name, const int &gender, const Date &birthday, const Date &deathday, string job, Person *father, Person *mother)
{
    init(name, gender, birthday, deathday, job, father, mother);
}

void Person::init(string name, const int &gender, const Date &birthday, const Date &deathday, string job, Person *father, Person *mother)
{
    _name = name;
    _gender = gender;
    _birthday = birthday;
    _deathday = deathday;
    _job = job;
    _pFather = father;
    _pMother = mother;
}

ArrayList<Person*> Person::getChildren(Person *spouse)
{
    ArrayList<Person*> children;
    if (_gender == kGenderFemale)
    {
        if (spouse == nullptr) return _children;
        int childAmount = _children.size();
        for (int index=0; index<childAmount; ++index)
        {
            Person *pChild = _children[index];
            if (pChild->getFather() == spouse)
            {
                children.add(pChild);
            }
        }
    }
    else if (_gender == kGenderMale)
    {
        // 遍历_spouses获取children
        int spouseAmount = _spouses.size();
        for (int index=0; index<spouseAmount; ++index)
        {
            Person *pSpouse = _spouses[index];
            if (spouse != nullptr && spouse != pSpouse)
            {
                continue;
            }
            ArrayList<Person*> cr = pSpouse->getChildren(this);
            children.addAll(cr);
        }

        // 没有指定母节点时，加上没有母节点的子节点
        if (spouse == nullptr)
            children.addAll(_children);
    }

    return children;
}

void Person::addChild(Person *child, Person *spouse)
{
    if (_gender == kGenderFemale)
    {
        child->setFather(spouse);
        child->setMother(this);
        _children.add(child);
    }
    else if (_gender == kGenderMale)
    {
        child->setFather(this);
        child->setMother(spouse);
        if (spouse != nullptr)
        {
            spouse->addChild(child, this);
        }
        else
        {
            _children.add(child);
        }
    }
}

void Person::marry(Person *spouse)
{
    addSpouse(spouse);
}

void Person::addSpouse(Person *spouse)
{
    _spouses.add(spouse);
}

Person* Person::getFather()
{
    return _pFather;
}

Person* Person::getMother()
{
    return _pMother;
}

void Person::setFather(Person *pa)
{
    _pFather = pa;
}

void Person::setMother(Person *ma)
{
    _pMother = ma;
}

string Person::toString()
{
    stringstream ss;
    ss << _name << "(" << _birthday.toString() << " ~ " << _deathday.toString() << ") " ;
    if (_gender == kGenderFemale) ss << "♀ ";
    else if (_gender == kGenderMale) ss << "♂ ";
    ss << _job;
    string info;
    std::getline(ss, info);
    return info;
}

#endif
